package com.ibm.spatiotemp.server.framework.processor.geoindexdb;

import java.text.ParseException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;

import org.apache.log4j.Logger;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.ibm.spatiotemp.datamodel.datatype.TemporalLineString;
import com.ibm.spatiotemp.datamodel.result.SpatioObjectResultSet;
import com.ibm.spatiotemp.processor.SearcherFactory;
import com.ibm.spatiotemp.processor.SpatialSearcher;
import com.ibm.spatiotemp.server.config.LocalConfig;
import com.ibm.spatiotemp.server.datamodel.gpsdata.geoIndexdb.GeoIndexTSPointResultSet;
import com.ibm.spatiotemp.server.datasources.DataSource;
import com.ibm.spatiotemp.server.framework.processor.SpatialQueryProcessor;
import com.ibm.spatiotemp.server.framework.reponse.ResponseFormatter;
import com.ibm.spatiotemp.server.framework.request.Request;
import com.ibm.spatiotemp.tools.DateParser;

import edu.umn.cs.spatialHadoop.core.Rectangle;

public class GeoIndexSpatialQueryProcessor extends SpatialQueryProcessor {

	public static Logger logger = Logger
			.getLogger(GeoIndexSpatialQueryProcessor.class);

	public GeoIndexSpatialQueryProcessor(DataSource src) {
		super(src);
	}

	@Override
	public String handlePositionQuery(Request request) throws Exception {
		return null;
	}

	private String handleTrajectoryQuery_Type1(Request request)
			throws ParseException {
		String retstring = "";
		String datasrc_string = request.getTenantid() + "/"
				+ request.getSourceid();
		String modid = request.getId();
		String startts = request.getStartts();
		String endts = request.getEndts();

		String ssid = request.getSessionid();

		long start = DateParser.getTimestamp(startts);
		long end = DateParser.getTimestamp(endts);
		long scale = LocalConfig.DefaultIndexTimeRangeScale;

		/**
		 * build RtreeSearcher
		 */
		Collection<String> modidset = new LinkedList<String>();
		modidset.add(modid);
		Map<String, SpatialSearcher[]> searchermap = SearcherFactory
				.getSpatialSearcher_2ndLayer(datasrc_string, modidset, start,
						end, scale);
		SpatialSearcher[] searchers = searchermap.get(modid);

		if (searchers == null || searchers.length == 0) {
			logger.warn("cannot find out relevent data");
			return "{}";
		}
		/*
		 * for type 1 query on index, not need to query index, just for raw time
		 * series data
		 */
		Rectangle rectangle = new Rectangle((-1) * Double.MAX_VALUE, (-1)
				* Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
		LinkedList<TemporalLineString> resultlist = new LinkedList<TemporalLineString>();
		for (int i = 0; i < searchers.length; i++) {
			SpatioObjectResultSet curresult;
			try {
				curresult = searchers[i].Range_Query(rectangle);
				resultlist.addAll(curresult.toList());
			} catch (Exception e) {
				logger.error(e);
			}
		}
		GeoIndexTSPointResultSet result = new GeoIndexTSPointResultSet(ssid,
				start, end, resultlist);
		retstring = ResponseFormatter
				.formatTSPointResultSetAsTimeandCoordinates(result)[0];
		return retstring;
	}

	private String handleTrajectoryQuery_Type2(Request request)
			throws ParseException {

		JsonNode bboxnode = request.getBbox();
		String datasrc_string = request.getTenantid() + "/"
				+ request.getSourceid();
		// String modid = request.getId();
		ArrayNode modidsnode = request.getIds();
		String startts = request.getStartts();
		String endts = request.getEndts();
		String ssid = request.getSessionid();

		long start = DateParser.getTimestamp(startts);
		long end = DateParser.getTimestamp(endts);
		long scale = LocalConfig.DefaultIndexTimeRangeScale;
		Rectangle rectangle = new Rectangle(bboxnode);

		/**
		 * step 1, find moving objects's ids via 1st layer index
		 */
		SpatialSearcher[] idxsearchers_1stlayer = SearcherFactory
				.getSpatialSearcher_1stLayer(datasrc_string, start, end, scale);
		if (idxsearchers_1stlayer == null || idxsearchers_1stlayer.length == 0) {
			return "{}";
		}

		LinkedList<TemporalLineString> resultlist = new LinkedList<TemporalLineString>();
		int startscale = (int) ((start) / (scale));
		long curstart = ((long) startscale) * scale;

		for (int i = 0; i < idxsearchers_1stlayer.length; i++) {
			/* find relevent modids */
			Collection<String> modids = null;
			try {
				modids = idxsearchers_1stlayer[i].Range_Query(rectangle)
						.getMovingObjectIDs();

				HashSet<String> overlap_modids = new HashSet<String>();
				if (modidsnode != null) {
					Iterator<JsonNode> iter = modidsnode.iterator();
					for (; iter.hasNext();) {
						String modid = iter.next().asText();
						if (modids.contains(modid))
							overlap_modids.add(modid);
					}
					modids.clear();
					modids = overlap_modids;
				}
			} catch (Exception e) {
				logger.error(e);
			}

			if (modids != null && !modids.isEmpty()) {
				Map<String, SpatialSearcher[]> searchermap = SearcherFactory
						.getSpatialSearcher_2ndLayer(datasrc_string, modids,
								curstart, curstart + scale - 1, scale);

				for (String modid : modids) {
					SpatialSearcher[] searchers = searchermap.get(modid);
					if (searchers == null || searchers.length == 0) {
						logger.warn("cannot find out relevent data");
					}
					for (int j = 0; j < searchers.length; j++) {
						SpatioObjectResultSet curresult = null;
						try {
							curresult = searchers[j].Range_Query(rectangle);
						} catch (Exception e) {
							logger.error(e);
						}
						resultlist.addAll(curresult.toList());
					}
				}

			}
			curstart = curstart + scale;
		}

		GeoIndexTSPointResultSet result = new GeoIndexTSPointResultSet(ssid,
				start, end, resultlist);
		
		String retstring = "[%s]";
		StringBuffer strbuf = new StringBuffer();
		String[] results = ResponseFormatter.formatTSPointResultSetAsPoints(result);
		int curidx = 0;
		for(curidx = 0; curidx < results.length; ++curidx)
		{
			if(curidx != 0)
			{
				strbuf.append(",");
				strbuf.append(results[curidx]);
			}
			else
				strbuf.append(results[curidx]);
		}
		retstring = String.format(retstring, strbuf.toString());
		
		return retstring;
		
	}

	@Override
	public String handleTrajectoryQuery(Request request) throws Exception {
		JsonNode bboxnode = request.getBbox();
		if (bboxnode == null)/* type1 query */
		{
			return handleTrajectoryQuery_Type1(request);
		} else {/* type 2 query */
			return handleTrajectoryQuery_Type2(request);
		}
	}

	@Override
	public String handleTimeQuery(Request request) throws Exception {
		return null;
	}

}
